package com.life.ai.server.modules.system.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.life.ai.server.common.constants.UserConstants;
import com.life.ai.server.common.domain.PageQuery;
import com.life.ai.server.common.exception.ClientException;
import com.life.ai.server.common.exception.ServiceException;
import com.life.ai.server.common.utils.AssertUtils;
import com.life.ai.server.framework.redis.annotation.RedisCache;
import com.life.ai.server.framework.redis.annotation.RedisEvict;
import com.life.ai.server.modules.system.entity.SysMenu;
import com.life.ai.server.modules.system.entity.SysRole;
import com.life.ai.server.modules.system.entity.SysUser;
import com.life.ai.server.modules.system.mapper.SysUserMapper;
import com.life.ai.server.modules.system.model.dto.ModifyPwdDTO;
import com.life.ai.server.modules.system.model.dto.SysUserDTO;
import com.life.ai.server.modules.system.model.dto.UpdateStatusDTO;
import com.life.ai.server.modules.system.model.dto.UserQueryDTO;
import com.life.ai.server.modules.system.model.vo.SysUserInfoVO;
import com.life.ai.server.modules.system.model.vo.SysUserVO;
import com.life.ai.server.modules.system.service.SysMenuService;
import com.life.ai.server.modules.system.service.SysRoleMenuService;
import com.life.ai.server.modules.system.service.SysUserRoleService;
import com.life.ai.server.modules.system.service.SysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author kyle
 * @since 2024-06-14
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private SysUserService     userService;
    @Resource
    private SysMenuService     menuService;
    @Resource
    private SysUserRoleService userRoleService;
    @Resource
    private SysRoleMenuService roleMenuService;

    /**
     * 获取用户信息(缓存)
     *
     * @param userId 用户id
     * @return SysUser
     */
    @RedisCache(name = UserConstants.SYS_USER, keys = "#userId")
    @Override
    public SysUser queryOne(Long userId) {
        return this.getById(userId);
    }

    /**
     * 获取当前登录用户信息
     *
     * @return SysUserInfoVO
     */
    @Override
    public SysUserInfoVO getUserInfoByUserId(Long userId) {
        SysUser sysUser = userService.getById(userId);
        SysUserInfoVO sysUserInfoVO = new SysUserInfoVO().setUser(sysUser);
        // 根据用户id获取角色列表
        List<SysRole> roles = userRoleService.listByUserId(userId);
        Set<String> roleSet = CollStreamUtil.toSet(roles, SysRole::getCode);
        sysUserInfoVO.setRoles(roleSet);

        List<SysMenu> sysMenus = new ArrayList<>();
        if (roleSet.contains(UserConstants.SUPPER_ADMIN_ROLE_CODE)) {
            // 超级管理员
            sysMenus = menuService.lambdaQuery().select(SysMenu::getPermission).list();
        } else {
            // 根据角色列表获取权限列表
            List<Long> roleIds = CollStreamUtil.toList(roles, SysRole::getId);
            sysMenus = roleMenuService.listByRoleIds(roleIds);
        }
        Set<String> permissions = CollStreamUtil.toSet(sysMenus, SysMenu::getPermission);
        sysUserInfoVO.setPermissions(permissions);
        return sysUserInfoVO;
    }

    /**
     * 分页查询
     *
     * @param pageQuery 分页参数
     * @param userQuery 查询条件
     * @return Page<SysUser>
     */
    @Override
    public Page<SysUserVO> queryPage(PageQuery pageQuery, UserQueryDTO userQuery) {
        Page<SysUser> userPage = baseMapper.queryPage(pageQuery.build(), userQuery);
        List<SysUserVO> sysUserVos = userPage.getRecords().stream().map(sysUser -> {
            List<SysRole> roleList = userRoleService.listByUserId(sysUser.getId());
            SysUserVO sysUserVo = BeanUtil.copyProperties(sysUser, SysUserVO.class);
            if (CollUtil.isNotEmpty(roleList)) {
                sysUserVo.setRoleList(roleList).setRoleIds(CollStreamUtil.toList(roleList, SysRole::getId));
            }
            return sysUserVo;
        }).collect(Collectors.toList());
        return new Page<SysUserVO>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal()).setRecords(sysUserVos);
    }

    /**
     * 新增用户
     *
     * @param sysUserDTO 新增信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addUser(SysUserDTO sysUserDTO) {
        // 校验唯一性
        checkUserNameUnique(sysUserDTO);
        checkUserPhoneUnique(sysUserDTO);
        // 设置默认密码
        String password = sysUserDTO.getPassword().trim();
        String salt = RandomUtil.randomString(UserConstants.PASSWORD_SALT_LENGTH);
        sysUserDTO.setSalt(salt);
        sysUserDTO.setPassword(SaSecureUtil.md5BySalt(password, salt));
        // 保存用户
        this.save(sysUserDTO);
        // 保存角色列表
        userRoleService.updateUserRole(sysUserDTO.getId(), sysUserDTO.getRoleIds());
    }

    /**
     * 修改用户
     *
     * @param sysUserDTO 修改信息
     */
    @RedisEvict(name = UserConstants.SYS_USER, keys = "#sysUserDTO.id")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUser(SysUserDTO sysUserDTO) {
        // 校验数据权限与唯一性
        checkUserAllowed(sysUserDTO);
        checkUserNameUnique(sysUserDTO);
        checkUserPhoneUnique(sysUserDTO);
        if (StrUtil.isNotBlank(sysUserDTO.getPassword())) {
            String salt = RandomUtil.randomString(UserConstants.PASSWORD_SALT_LENGTH);
            sysUserDTO.setSalt(salt);
            sysUserDTO.setPassword(SaSecureUtil.md5BySalt(sysUserDTO.getPassword().trim(), salt));
        }
        this.updateById(sysUserDTO);
        // 更新关联角色列表
        userRoleService.updateUserRole(sysUserDTO.getId(), sysUserDTO.getRoleIds());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(UpdateStatusDTO updateStatusDTO) {
        String status = updateStatusDTO.getStatus();
        if(ObjectUtil.isEmpty(updateStatusDTO.getIds())) {
            throw new ClientException("ids不能为空");
        }
        updateStatusDTO.getIds().forEach(id -> {
            updateById(SysUser.of(id).setStatus(status));
        });
    }

    /**
     * 校验用户名是否唯一
     *
     * @param user 用户信息
     */
    private void checkUserNameUnique(SysUser user) {
        boolean exists = lambdaQuery().eq(SysUser::getUsername, user.getUsername())
                .ne(ObjectUtil.isNotNull(user.getId()), SysUser::getId, user.getId()).exists();
        AssertUtils.isTrue(!exists, "用户名【{}】已注册", user.getUsername());
    }

    /**
     * 校验手机号是否唯一
     *
     * @param user 用户信息
     */
    private void checkUserPhoneUnique(SysUser user) {
        boolean exists = lambdaQuery().eq(SysUser::getPhone, user.getPhone())
                .ne(ObjectUtil.isNotNull(user.getId()), SysUser::getId, user.getId()).exists();
        AssertUtils.isTrue(!exists, "手机号【{}】已注册", user.getPhone());
    }


    private void checkUserAllowed(SysUser user) {
        if (ObjectUtil.isNotNull(user.getId()) && user.isSuperAdmin()) {
            throw new ServiceException("不允许操作默认系统管理员");
        }
    }

    /**
     * 删除用户
     *
     * @param userId 用户id
     */
    @RedisEvict(name = UserConstants.SYS_USER, keys = "#userId")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long userId) {
        checkUserAllowed(SysUser.of(userId));
        // 删除用户与角色关联
        userRoleService.removeByUserId(userId);
        // 删除用户
        this.removeById(userId);
        // 踢出当前登录用户
        StpUtil.kickout(userId);
    }

    /**
     * 批量删除用户
     *
     * @param userIds 用户id集合
     */
    @Override
    public void deleteBatch(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return;
        }
        userIds.forEach(userId -> userService.delete(userId));
    }

    /**
     * 重置密码
     *
     * @param userId 用户id
     */
    @Override
    public void resetPwd(Long userId) {
        checkUserAllowed(SysUser.of(userId));
        SysUser sysUser = this.getById(userId);
        userService.updatePassword(userId, sysUser.getPhone());
    }

    /**
     * 修改密码
     *
     * @param modifyPwdDTO 修改密码参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void modifyPwd(ModifyPwdDTO modifyPwdDTO) {
        Long userId = StpUtil.getLoginIdAsLong();
        SysUser sysUser = this.getById(userId);
        // 校验旧密码
        String loginPwd = SaSecureUtil.md5BySalt(modifyPwdDTO.getOldPassword(), sysUser.getSalt());
        AssertUtils.isTrue(loginPwd.equals(sysUser.getPassword()), "旧密码输入错误");
        // 更新密码
        userService.updatePassword(userId, modifyPwdDTO.getNewPassword());
    }

    /**
     * 修改密码
     *
     * @param userId      用户id
     * @param newPassword 新密码
     */
    @Override
    public void updatePassword(Long userId, String newPassword) {
        // 生成随机盐值
        String salt = RandomUtil.randomString(UserConstants.PASSWORD_SALT_LENGTH);
        lambdaUpdate().eq(SysUser::getId, userId)
                .set(SysUser::getSalt, salt)
                .set(SysUser::getPassword, SaSecureUtil.md5BySalt(newPassword.trim(), salt))
                .update();
    }
}

